﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using NLog;

namespace Jet.VersionIncrementor
{
	/// <summary>
	/// Parse C# projects and assign versions
	/// </summary>
	internal class CSharpProjectParser : BaseProjectParser
	{
		private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

		/// <summary>
		/// .ctor
		/// </summary>
		/// <param name="fileName">Path to .csproj file</param>
		public CSharpProjectParser(string fileName)
			:base(fileName)
		{
		}

		private string FindAssemblyInfo()
		{
			var nodes = SelectProjectNodes("/ns:Project/ns:ItemGroup/ns:Compile[contains(@Include,'AssemblyInfo.cs')]");
			var assemblyInfoFileName = String.Empty;
			foreach (XmlNode node in nodes)
			{
				if (node.Attributes != null)
				{
					var file = node.Attributes["Include"].InnerText;
					assemblyInfoFileName = Path.Combine(RootDir, file);
				}
			}
			if (String.IsNullOrEmpty(assemblyInfoFileName))
			{
				throw new Exception("Wrong assembly info file");
			}
			_logger.Debug("Project file '{0}' parsed, found '{1}'", ProjectFileName, assemblyInfoFileName);
			return assemblyInfoFileName;
		}

		private Version GetCurrentVersion()
		{
			var assemblyInfoFileName = FindAssemblyInfo();
			_logger.Debug("Retrieving version from project '{0}'", ProjectFileName);
			var line = File.ReadAllLines(assemblyInfoFileName).FirstOrDefault(l => l.Contains("AssemblyFileVersion")
				&& !l.Trim().Trim('\t').StartsWith("//"));
			if (String.IsNullOrEmpty(line))
			{
				return new Version(0, 0, 0, 0);
			}
			var start = line.IndexOf('"') + 1;
			var end = line.LastIndexOf('"');
			var version = line.Substring(start, end - start);
			_logger.Debug("Project '{0}' has version '{1}'", ProjectFileName, version);
			var result = new Version(version);
			return result;
		}

		private void SetVersion(Version version)
		{
			_logger.Debug("Setting version for project '{0}'", ProjectFileName);
			var assemblyInfoFileName = FindAssemblyInfo();
			var lines = File.ReadAllLines(assemblyInfoFileName);
			var result = new List<string>();
			var assemblyFileVersionExists = false;
			var assemblyInformationalVersionExists = false;
			var assemblyVersionExists = false;
			foreach (var line in lines)
			{
				if (line.Trim().Trim('\t').StartsWith("//"))
				{
					result.Add(line);
				}
				else if (line.Contains("AssemblyFileVersion"))
				{
					result.Add(String.Format("[assembly: AssemblyFileVersion(\"{0}\")]", version));
					assemblyFileVersionExists = true;
					_logger.Debug("For project '{0}' assemblyFileVersionExists exists", ProjectFileName);
				}
				else if (line.Contains("AssemblyInformationalVersion"))
				{
					result.Add(String.Format("[assembly: AssemblyInformationalVersion(\"{0}\")]", version));
					assemblyInformationalVersionExists = true;
					_logger.Debug("For project '{0}' assemblyInformationalVersionExists exists", ProjectFileName);
				}
				else if (line.Contains("AssemblyVersion"))
				{
					//only major-minor part of the version. See details here:
					//http://stackoverflow.com/questions/64602/what-are-differences-between-assemblyversion-assemblyfileversion-and-assemblyinf
					result.Add(String.Format("[assembly: AssemblyVersion(\"{0}.{1}.0.0\")]", version.Major, version.Minor));
					assemblyVersionExists = true;
					_logger.Debug("For project '{0}' assemblyVersionExists exists", ProjectFileName);
				}
				else
				{
					result.Add(line);
				}
			}
			if (!assemblyFileVersionExists)
			{
				result.Add(String.Format("[assembly: AssemblyFileVersion(\"{0}\")]", version));
			}
			if (!assemblyInformationalVersionExists)
			{
				result.Add(String.Format("[assembly: AssemblyInformationalVersion(\"{0}\")]", version));
			}
			if (!assemblyVersionExists)
			{
				result.Add(String.Format("[assembly: AssemblyVersion(\"{0}.{1}.0.0\")]", version.Major, version.Minor));
			}
			File.WriteAllLines(assemblyInfoFileName, result);
			_logger.Debug("Version '{0}' was assigned foir project {1}", version, ProjectFileName);
		}

		public override Version CurrentVersion
		{
			get
			{
				return GetCurrentVersion();
			}
			set
			{
				SetVersion(value);
			}
		}
	}
}
